#include "stlslicewidget.h"

STLSliceWidget::STLSliceWidget(std::shared_ptr<STLReader> stl_reader, const SliceInfo& slice_info, QWidget *parent)
{
    m_sp_stl_reader = stl_reader;
    assert(slice_info.plane_norm != QVector3D());
    m_plane_norm = slice_info.plane_norm;
    m_camera_dis = slice_info.distance;
    m_camera_up = slice_info.camera_up;
    m_stride = slice_info.thickness;
    m_p_program = new QOpenGLShaderProgram(this);
    m_sp_model_edges_convertor = std::make_shared<EdgesConvertor>();
}

STLSliceWidget::~STLSliceWidget()
{
    delete m_p_program;
}

QVector3D STLSliceWidget::getCurrentCameraCenter()
{
    return m_camera.getCenter();
}

QVector3D STLSliceWidget::getCurrentCameraUp()
{
    return m_camera.getUp();
}

QVector3D STLSliceWidget::getCurrentCameraPos()
{
    return m_camera.getEye();
}

float STLSliceWidget::getCurrentSliceDepth()
{
    return m_cur_slice_lambda;
}

QVector<QVector<QVector<Vertex>>> STLSliceWidget::getModelContours()
{
    const QVector<Mesh>& model_meshes = m_sp_stl_reader->getModelMeshes();
    qsizetype numSlices = m_slice_lambdas.size();

    QVector<QVector<QVector<Vertex>>> resultant_contours(numSlices);
    std::for_each(std::execution::par, resultant_contours.begin(), resultant_contours.end(),
        [&](auto& slice) {
            int i = &slice - &resultant_contours[0];
            QVector<QVector<Vertex>> contours_3d;
            QVector<Edge> contour_edges;
            QVector<Mesh> proper_meshes = h0(model_meshes, m_plane_norm, m_slice_lambdas[i]);
            h1(proper_meshes, m_plane_norm, m_slice_lambdas[i], contour_edges);
            h2(contour_edges, contours_3d);
            slice = std::move(contours_3d);
        }
    );

    return resultant_contours;
}

#include <QJsonArray>
#include <QJsonDocument>
#include <QFile>

void saveContoursToJson(
    const QVector<QVector<QVector<QPointF>>> &resultant_contours,
    const QString &filename)
{
    QJsonArray json_result;

    for (const auto &slice : resultant_contours) {
        QJsonArray slice_array;
        for (const auto &contour : slice) {
            QJsonArray contour_array;
            for (const auto &pt : contour) {
                QJsonArray point_array;
                point_array.append(pt.x());
                point_array.append(pt.y());
                contour_array.append(point_array);
            }
            slice_array.append(contour_array);
        }
        json_result.append(slice_array);
    }

    QJsonDocument doc(json_result);
    QFile file(filename);
    if (file.open(QIODevice::WriteOnly)) {
        file.write(doc.toJson(QJsonDocument::Indented));
        file.close();
    }
}

QVector<QVector<QVector<QPointF>>> STLSliceWidget::getModelContours2D()
{
    const QVector<Mesh>& model_meshes = m_sp_stl_reader->getModelMeshes();
    qsizetype numSlices = m_slice_lambdas.size();

    // 注意世界坐标系（右手系） 与 视图坐标系（左手系） 的手性不同，使用相机(eye center up)重构垂直于相机视角向量投影平面时
    // 由于被投影点都是世界坐标表示的（右手系），重构投影平面时应当是世界坐标的平面而不是相机坐标系
    // z_world = center - eye, x_world = up × z_world, y_world = z_world × x_world

    QVector3D u_axis = QVector3D::crossProduct(m_camera_up, m_plane_norm).normalized();
    QVector3D v_axis = QVector3D::crossProduct(m_plane_norm, u_axis).normalized();

    QVector<QVector<QVector<QPointF>>> resultant_contours(numSlices);
    std::for_each(std::execution::par, resultant_contours.begin(), resultant_contours.end(),
        [&](auto& slice) {
            int i = &slice - &resultant_contours[0];
            QVector<QVector<Vertex>> contours_3d;
            QVector<Edge> contour_edges;
            QVector<Mesh> proper_meshes = h0(model_meshes, m_plane_norm, m_slice_lambdas[i]);
            QVector<QVector<QPointF>> slice_contours_2d;
            h1(proper_meshes, m_plane_norm, m_slice_lambdas[i], contour_edges);
            h2(contour_edges, contours_3d);
            for (const auto& contour : contours_3d) {
                QVector<QPointF> contour_2d;
                for (const Vertex& vertex : contour) {
                    float u = QVector3D::dotProduct(vertex.pos, u_axis);
                    float v = QVector3D::dotProduct(vertex.pos, v_axis);
                    contour_2d.append(QPointF(u, v));
                }
                slice_contours_2d.append(contour_2d);
            }
            slice = std::move(slice_contours_2d);
        }
    );

    // 保存点集为JSON格式，方便Python读取显示
    saveContoursToJson(resultant_contours, "contours.json");

    return resultant_contours;
}

void STLSliceWidget::wheelEvent(QWheelEvent *event)
{
    updateSliceIndexRangeInfo(m_sliceHead, m_slicePointsCount, event->angleDelta().y() > 0.0f ? true : false);
    update();
}

void STLSliceWidget::initializeGL()
{
    initializeOpenGLFunctions();
    glEnable(GL_DEPTH_TEST);
    glClearColor(0.3f, 0.5f, 1.0f, 1.0f);
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

    // 初始化摄像机
    // TODO（mengjinlang）: 在没搞明白为什么切片视角，坐标系视角和模型视角为什么统一了的数学原理前，别改动这个相机初始化参数
    m_camera.setPerspective(static_cast<float>(width()) / static_cast<float>(height()), 0.1f, 100.0f);
    m_camera.setMouseLockedStatus(true);
    // QVector3D up(0.0f, 1.0f, 0.0f);
    // if (qFuzzyCompare(qAbs(QVector3D::dotProduct(m_plane_norm.normalized(), up)), 1.0f))
    //     up = QVector3D(1.0f, 0.0f, 0.0f);
    m_camera.lookAt(m_camera_dis * m_plane_norm, QVector3D(0, 0, 0), m_camera_up);

    // 编译和链接着色器
    if(!compileAndLinkShaders(m_p_program, ":/vertex_shaders/shaders_code/vs/vertex_shader.vs", ":/fragment_shaders/shaders_code/fs/fragment_shader.fs"))
    {
        qDebug() << "Failed to compile and link shaders.";
        return;
    }

    // 将模型拉到视野中心
    calAndSetModelInitPhase();

    /* 边数据 */
    // meshes 转 edges
    m_sp_model_edges_convertor->convertFrom(m_sp_stl_reader->getModelMeshes());

    // 获取edges数据
    QVariant model_data = m_sp_model_edges_convertor->getModel();
    QVector<Edge> model_edges = model_data.value<QVector<Edge>>();

    /* 边数据切片 */
    // 预处理边向量区间
    auto preprocessed_partitions = m_stl_model_slices_producer.preprocessModelDataAlongDirection(model_edges, m_plane_norm);

    // 求面片与平面的所有交点
    QVector<Vertex> intersectedPoints;
    m_stl_model_slices_producer.generateShownVertices(preprocessed_partitions, m_plane_norm, m_stride, intersectedPoints, m_intersected_points_counts, m_slice_lambdas);

    setSliceShaderProgramDataFormation();

    setSliceVertexData2Shader(intersectedPoints);
}

void STLSliceWidget::paintGL()
{
    glViewport(0, 0, this->width(), this->height());
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除颜色和深度缓冲
    m_vao.bind();
    m_p_program->bind();

    m_p_program->setUniformValue("model", m_model);
    m_p_program->setUniformValue("view", m_camera.getViewMatrix());
    m_p_program->setUniformValue("projection", m_camera.getProjectionMatrix());

    // 设置法线矩阵
    QMatrix3x3 normModelMatrix = m_model.normalMatrix();
    m_p_program->setUniformValue("normModelMatrix", normModelMatrix);

    glDrawArrays(GL_POINTS, m_sliceHead, m_slicePointsCount);
    m_p_program->release();
    m_vao.release();
}

void STLSliceWidget::resizeGL(int w, int h)
{
    glViewport(0, 0, w, h);
}

bool STLSliceWidget::loadShaderSource(const QString &filePath, QString &source)
{
    QFile file(filePath);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        qDebug() << "Failed to open shader file:" << filePath;
        return false;
    }

    QTextStream stream(&file);
    source = stream.readAll();
    file.close();
    return true;
}

bool STLSliceWidget::compileAndLinkShaders(QOpenGLShaderProgram *p_program, const QString& vs_file_path, const QString& fs_file_path)
{
    if(p_program == nullptr)
        return false;

    QString vertexSource, fragmentSource;

    // 加载顶点着色器代码
    if (!loadShaderSource(vs_file_path, vertexSource))
    {
        return false;
    }

    // 加载片段着色器代码
    if (!loadShaderSource(fs_file_path, fragmentSource))
    {
        return false;
    }

    // 编译顶点着色器
    if (!p_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexSource))
    {
        qDebug() << "Vertex shader compile error:" << p_program->log();
        return false;
    }

    // 编译片段着色器
    if (!p_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentSource))
    {
        qDebug() << "Fragment shader compile error:" << p_program->log();
        return false;
    }

    // 链接着色器程序
    if (!p_program->link())
    {
        qDebug() << "Shader program link error:" << p_program->log();
        return false;
    }

    return true;
}

void STLSliceWidget::setSliceShaderProgramDataFormation()
{
    if(m_p_program == nullptr)
    {
        qDebug()<<"Shader promgram has not been initialized. Abort building shader program.";
        return;
    }
    m_vao.create();
    m_vao.bind();

    m_vbo.create();
    m_vbo.bind();

    m_p_program->bind();
    m_p_program->enableAttributeArray(0);
    m_p_program->setAttributeBuffer(0, GL_FLOAT, offsetof(Vertex, pos), 3, sizeof(Vertex));

    m_p_program->enableAttributeArray(1);
    m_p_program->setAttributeBuffer(1, GL_FLOAT, offsetof(Vertex, norm), 3, sizeof(Vertex));

    m_p_program->enableAttributeArray(2);
    m_p_program->setAttributeBuffer(2, GL_FLOAT, offsetof(Vertex, color), 4, sizeof(Vertex));

    m_vao.release();
    m_vbo.release();
    m_p_program->release();
}

void STLSliceWidget::setSliceVertexData2Shader(const QVector<Vertex>& slice_vertices)
{
    m_vbo.bind();
    assert(slice_vertices.size() * sizeof(Vertex) <= INT_MAX);
    m_vbo.allocate(slice_vertices.constData(), slice_vertices.size() * sizeof(Vertex)); // 如果顶点数量大于int的最大值，此处会异常
    m_vbo.release();
}

void STLSliceWidget::updateSliceIndexRangeInfo(GLint &slice_head, GLint &count, bool increaseOrDecrease)
{
    if(!m_bIsinitializedPerformance)
    {
        count = m_intersected_points_counts[0];
        slice_head = 0;
        m_cur_slice_lambda = m_slice_lambdas[0];
        emit sendSliceNumberStatus(1, m_slice_lambdas.size());
        m_bIsinitializedPerformance = true;
        return;
    }
    if (increaseOrDecrease && m_curShowSliceIndice + 1 < m_intersected_points_counts.size())
    {
        m_curShowSliceIndice++;
        m_cur_slice_lambda = m_slice_lambdas[m_curShowSliceIndice];
        count = m_intersected_points_counts[m_curShowSliceIndice];
        slice_head += (m_curShowSliceIndice == 0) ? 0 : m_intersected_points_counts[m_curShowSliceIndice - 1];
        emit sendSliceNumberStatus(m_curShowSliceIndice + 1, m_slice_lambdas.size());
    }
    else if (!increaseOrDecrease && m_curShowSliceIndice > 0)
    {
        m_curShowSliceIndice--;
        m_cur_slice_lambda = m_slice_lambdas[m_curShowSliceIndice];
        count = m_intersected_points_counts[m_curShowSliceIndice];
        slice_head -= /*(m_curShowSliceIndice == 0) ? 0 : */m_intersected_points_counts[m_curShowSliceIndice];
        emit sendSliceNumberStatus(m_curShowSliceIndice + 1, m_slice_lambdas.size());
    }
}

void STLSliceWidget::calAndSetModelInitPhase(bool isFlip)
{
    // 计算与设置STL模型初始相位
    m_centroid = m_model_attr_calculator.calModelCentroid(m_sp_stl_reader->getModelMeshes());
    m_model.translate(-m_centroid.x(), -m_centroid.y(), -m_centroid.z());
}
